﻿namespace Pap.Core.CronJobs
{
    using System;
    using System.Collections;
    using System.Configuration;
    using System.Diagnostics;
    using System.Threading;
    using Microsoft.Practices.ServiceLocation;

    using Pap.Core.Logging;

    /// <summary>
    /// Represents the Scheduler.
    /// </summary>
    public class Scheduler
    {
        #region Fields

        private static Scheduler.Agent[] m_agents;
        private static TimeSpan m_interval;
        private static object m_lock;
        private static DateTime m_startTime;

        #endregion Fields

        #region Constructors

        static Scheduler()
        {
            Scheduler.m_lock = new object();
            Trace.TraceInformation("Scheduler - Initializing");
            Scheduler.m_startTime = DateTime.Now;
            string @string = ConfigurationManager.AppSettings["Scheduler.Frequency"];
            Scheduler.m_interval = TimeSpan.Parse(@string);
            if (Scheduler.m_interval.TotalSeconds > 0.0)
            {
                Trace.TraceInformation("Scheduler - Interval set to: " + Scheduler.m_interval);
                Thread thread = new Thread(new ThreadStart(Scheduler.WorkLoop));
                thread.Start();
                Trace.TraceInformation("Scheduler - Worker thread started");
                return;
            }
            Trace.TraceInformation("Scheduler - Scheduling is disabled (interval is 00:00:00)");
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets the agents.
        /// </summary>
        /// <value>The agents.</value>
        private static Scheduler.Agent[] Agents
        {
            get
            {
                if (Scheduler.m_agents == null)
                {
                    object @lock;
                    Monitor.Enter(@lock = Scheduler.m_lock);
                    try
                    {
                        if (Scheduler.m_agents == null)
                        {
                            Scheduler.m_agents = Scheduler.ReadAgents();
                        }
                    }
                    finally
                    {
                        Monitor.Exit(@lock);
                    }
                }
                if (Scheduler.m_agents != null)
                {
                    return Scheduler.m_agents;
                }
                return new Scheduler.Agent[0];
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public static void Initialize()
        {
        }

        /// <summary>
        /// Runs the processor.
        /// </summary>
        public static void Process()
        {
            object @lock;
            Monitor.Enter(@lock = Scheduler.m_lock);
            try
            {
                Scheduler.Agent[] agents = Scheduler.Agents;
                Scheduler.Agent[] array = agents;
                for (int i = 0; i < array.Length; i++)
                {
                    Scheduler.Agent agent = array[i];
                    try
                    {
                        if (agent.IsDue)
                        {
                            agent.Execute();
                        }
                    }
                    catch (Exception exception)
                    {
                        ServiceLocator.Current.GetInstance<ILog>().RaiseError(exception);
                    }
                }
            }
            finally
            {
                Monitor.Exit(@lock);
            }
        }

        /// <summary>
        /// Reads the agents.
        /// </summary>
        /// <returns></returns>
        private static Scheduler.Agent[] ReadAgents()
        {
            Trace.TraceInformation("Scheduler - Adding agents");
            try
            {
                var arrayList = new ArrayList();
                var cronJobs = ServiceLocator.Current.GetAllInstances<ICronJob>();
                foreach (var cronJob in cronJobs)
                {
                    try
                    {
                        TimeSpan timeSpan = cronJob.SlidingTime;
                        string string2 = cronJob.JobName;

                        if (timeSpan.TotalSeconds > 0.0)
                        {
                            Trace.TraceInformation(string.Concat(new object[]
                                                                     {
                                                                         "Scheduler - Adding agent: ",
                                                                         string2,
                                                                         " (interval: ",
                                                                         timeSpan,
                                                                         ")"
                                                                     }));
                            Scheduler.Agent value = new Scheduler.Agent(string2, cronJob, timeSpan,
                                                                        Scheduler.m_startTime);
                            arrayList.Add(value);
                        }
                        else
                        {
                            Trace.TraceInformation("Scheduler - Skipping inactive agent: " + string2);
                        }
                    }
                    catch (Exception exception)
                    {
                        ServiceLocator.Current.GetInstance<ILog>().RaiseError(exception);
                    }
                }
                Trace.TraceInformation("Scheduler - Agents added");
                return arrayList.ToArray(typeof(Scheduler.Agent)) as Scheduler.Agent[];
            }
            catch (Exception exception2)
            {
                ServiceLocator.Current.GetInstance<ILog>().RaiseError(exception2);
            }
            return null;
        }

        /// <summary>
        /// Main scheduler loop
        /// </summary>
        private static void WorkLoop()
        {
            while (true)
            {
                Thread.Sleep(Scheduler.m_interval);
                try
                {
                    Scheduler.Process();
                }
                catch(Exception ex)
                {
                    ServiceLocator.Current.GetInstance<ILog>().RaiseError(ex);
                }
            }
        }

        #endregion Methods

        #region Nested Types

        /// <summary>
        /// Represents an Agent.
        /// </summary>
        protected class Agent
        {
            #region Fields

            private TimeSpan m_interval;
            private DateTime m_lastRun;
            private string m_name;
            private ICronJob m_object;

            #endregion Fields

            #region Constructors

            /// <summary>
            /// Initializes a new instance of the <see cref="T:Pap.Core.CronJobs.Scheduler.Agent" /> class.
            /// </summary>
            /// <param name="name">The name.</param>
            /// <param name="obj">The object.</param>
            /// <param name="method">The method.</param>
            /// <param name="interval">The interval.</param>
            /// <param name="lastRun">The last run.</param>
            public Agent(string name, ICronJob obj, TimeSpan interval, DateTime lastRun)
            {
                this.m_name = name;
                this.m_object = obj;
                this.m_interval = interval;
                this.m_lastRun = lastRun;
            }

            #endregion Constructors

            #region Properties

            /// <summary>
            /// Gets a value indicating whether this instance is due.
            /// </summary>
            /// <value><c>true</c> if this instance is due; otherwise, <c>false</c>.</value>
            public bool IsDue
            {
                get
                {
                    DateTime now = DateTime.Now;
                    return now - this.m_lastRun > this.m_interval;
                }
            }

            /// <summary>
            /// Gets the name.
            /// </summary>
            /// <value>The name.</value>
            public string Name
            {
                get
                {
                    return this.m_name;
                }
            }

            #endregion Properties

            #region Methods

            /// <summary>
            /// Executes this instance.
            /// </summary>
            public void Execute()
            {
                try
                {
                    Trace.TraceInformation(this.m_name + " is Executing");
                    this.m_object.Execute();
                }
                finally
                {
                    this.m_lastRun = DateTime.Now;
                }
            }

            #endregion Methods
        }

        #endregion Nested Types
    }
}